SciChart WPF 2D Charts > Screenshots, Printing and Export > Export How-To
Export How-To

Exporting to Vector Formats (PDF, SVG, XPS)

Exporting to a vector format enables the creation of resolution-independent graphics, ideal for printing or embedding in high-quality documents.

Unlike raster formats such as PNG, JPEG, or BMP, vector exports preserve lines and shapes as true vector elements, allowing charts to remain crisp and sharp at any zoom level.

While SciChart does not currently support direct export to SVG or PDF, it does allow exporting charts to the XPS (XML Paper Specification) vector format:

Exporting to XPS
Copy Code
// Enables higher resolution export with XamlRenderSurface (SDK/Enterprise only)
bool useXamlRenderSurface = false;
// Target export size, or null for current chart size
Size? exportedSize = null;
// Save the current chart visual to an XPS file
// "What you see is what you get": the visible chart will be exported
sciChartSurface.ExportToFile("chart-output.xps", ExportType.Xps, useXamlRenderSurface, exportedSize);
NOTE: Please be aware that XPS format is obsolete. Since Microsoft has retired support for the XPS file format, we cannot guarantee that XPS export works correctly on all devices.

Export to PDF

You can print a chart to PDF using the standard PrintDialog along with a virtual printer such as “Microsoft Print to PDF”. This approach exports exactly what is visible on screen as a raster image embedded in a PDF:

Printing to PDF
Copy Code
// Shows the OS Print dialog; pick "Microsoft Print to PDF"
sciChartSurface.Print("My Chart Export");

Alternatively, you can export the chart to SciChart’s supported XPS vector format and then convert the XPS file to PDF using a third-party library or tool. There are also a number of online services available that support XPS to PDF conversion.

Exporting Multiple Charts to a Single Image

In some cases, it may be desirable to present multiple related charts together — for example, to share a dashboard snapshot, generate a combined report, or archive visual analytics.

To achieve this with SciChart, we recommend exporting all charts to bitmaps using the SciChart API, and then composing them into a custom layout in code using WPF capabilities.

The example below demonstrates how to export four charts into a 2×2 grid. This is accomplished through the following steps:

  1. Export each chart to a bitmap using the SciChart export API
  2. Create a WPF DrawingVisual — a lightweight visual object for drawing vector and bitmap graphics
  3. Draw the exported bitmaps onto the DrawingVisual in the desired layout
  4. Render the DrawingVisual to a RenderTargetBitmap
  5. Save the RenderTargetBitmap to a PNG (or any other supported image format)

The following method demonstrates this process:

Exporting multiple charts to a grid
Copy Code
public static void Export2x2(
        SciChartSurface scs1, SciChartSurface scs2,
        SciChartSurface scs3, SciChartSurface scs4,
        string outputPath,
        int dpi = 96,
        int cellGap = 0) // optional spacing between cells
    {
        // Export each visible surface to an bitmap
        var b1 = scs1.ExportToBitmapSource();
        var b2 = scs2.ExportToBitmapSource();
        var b3 = scs3.ExportToBitmapSource();
        var b4 = scs4.ExportToBitmapSource();
        // Column widths are the max of each column
        // row heights are the max of each row
        int col1Width = Math.Max(b1.PixelWidth,  b3.PixelWidth);
        int col2Width = Math.Max(b2.PixelWidth,  b4.PixelWidth);
        int row1Height = Math.Max(b1.PixelHeight, b2.PixelHeight);
        int row2Height = Math.Max(b3.PixelHeight, b4.PixelHeight);
        // Gaps between cells
        int totalWidth  = col1Width + col2Width + (cellGap > 0 ? cellGap : 0);
        int totalHeight = row1Height + row2Height + (cellGap > 0 ? cellGap : 0);
        // Composite with DrawingVisual → RenderTargetBitmap
        var dv = new DrawingVisual();
        using (var dc = dv.RenderOpen())
        {
            dc.DrawImage(b1, new Rect(0, 0, col1Width, row1Height));
            dc.DrawImage(b2, new Rect(col1Width + cellGap, 0, col2Width, row1Height));
            dc.DrawImage(b3, new Rect(0, row1Height + cellGap, col1Width, row2Height));
            dc.DrawImage(b4, new Rect(col1Width + cellGap, row1Height + cellGap, col2Width, row2Height));
        }
        var rtb = new RenderTargetBitmap(totalWidth, totalHeight, dpi, dpi, PixelFormats.Pbgra32);
        rtb.Render(dv);
        // Encode to PNG
        var enc = new PngBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(rtb));
        using var fs = File.Create(outputPath);
        enc.Save(fs);
    }

Exporting Multiple Charts to Separate Images

When you need to distribute or archive each chart separately — for example, to enable independent editing, embed visuals into different documents, or automate per-chart reporting — it is often more practical to export each chart as an individual image file, rather than combining them into a single graphic.

Export existing on-screen surfaces (as-is)

Export on-screen surfaces
Copy Code
// Assume you have a list of surfaces already in the UI
IList<SciChartSurface> surfaces = new List<SciChartSurface> { surfaceA, surfaceB, surfaceC };
string outputDir = Path.Combine(Environment.CurrentDirectory, "Exports");
Directory.CreateDirectory(outputDir);
for (int i = 0; i < surfaces.Count; i++)
{
    var surface = surfaces[i];
    string filePath = Path.Combine(outputDir, $"chart_{i + 1:D3}.png");
    // export as it is, no size
    // but can be called with cloning as well
    surface.ExportToFile(filePath, ExportType.Png, false);
}

Export N off-screen charts(headless) with numbered names

Export off-screen charts with numbered names
Copy Code
// Pick a target render size for all images
var size = new Size(1600, 900);
string outputDir = Path.Combine(Environment.CurrentDirectory, "OffscreenExports");
Directory.CreateDirectory(outputDir);
// Let’s say we need to export 5 charts
int count = 5;
for (int i = 0; i < count; i++)
{
    string filePath = Path.Combine(outputDir, $"offscreen_{i + 1:D3}.png");
    // Build a surface entirely in code for this iteration (axes + series, etc.)
    var surface = OffscreenBuildSurfaceForIndex(i, size);
    // Because it’s off-tree, initialize and layout manually
    surface.OnLoad();
    surface.Measure(size);
    surface.Arrange(new Rect(0, 0, size.Width, size.Height));
    surface.UpdateLayout();
    // Export to file (PNG/JPEG/BMP/XPS supported)
    surface.ExportToFile(filePath, ExportType.Png, useXamlRenderSurface: false, exportedSize: size);
}

// Method used for creating SciChartSurface in codebehind
static SciChartSurface OffscreenBuildSurfaceForIndex(int index, Size size)
{
    var s = new SciChartSurface { Width = size.Width, Height = size.Height };
    // Axes
    s.XAxes.Add(new SciChart.Charting.Visuals.Axes.NumericAxis());
    s.YAxes.Add(new SciChart.Charting.Visuals.Axes.NumericAxis());
    // Data & series (vary per index as needed)
    var ds = new SciChart.Charting.Model.DataSeries.XyDataSeries<double, double>
             { SeriesName = $"Series {index + 1}" };
    for (int x = 0; x < 1000; x++)
    {
        ds.Append(x, Math.Sin((x + index * 50) * 0.02));
        s.RenderableSeries.Add(new
                 SciChart.Charting.Visuals.RenderableSeries.FastLineRenderableSeries  
                 { DataSeries = ds });
    }
    return s;
}

Exporting an Entire View That Contains a Chart

SciChart does not provide built-in functionality for exporting an entire view. However, you can achieve this by capturing the entire container — such as a Window, UserControl, or Grid — that hosts the SciChartSurface along with any other UI elements.

The example below demonstrates this approach using WPF’s RenderTargetBitmap to render and export a UI container to an image:

Capturing the entire container
Copy Code
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
// Assume 'container' is a Grid containing { SciChartSurface, Legend, Buttons, ... }
// and is currently visible on screen.
var rtb = new RenderTargetBitmap(
    (int)container.ActualWidth,
    (int)container.ActualHeight,
    96, 96, PixelFormats.Pbgra32);
rtb.Render(container);
var enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(rtb));
using (var fs = File.Create("container_export.png"))
{
    enc.Save(fs);
}

Exporting Charts from Multiple Tabs

When your WPF application organizes charts inside a TabControl, you might need to save each chart as an image—even those on tabs that aren’t currently visible. Exporting charts directly from the TabControl lets you capture every tab’s content programmatically, ensuring that all charts are saved with consistent quality without requiring the user to switch tabs manually.

In WPF, exporting charts from hidden tabs is not straightforward:

  • Controls in non-selected tabs are often unloaded or never measured/arranged, which makes a simple “as-is” bitmap capture unreliable.
  • Attempting to render these visuals directly may result in empty or partially drawn images.

Below is an example of how you can implement a solution to this problem:

Exporting from multiple Tabs
Copy Code
public void ExportAllCharts()
{
    var size  = new Size(1600, 900);
    var outDir = Path.Combine(Environment.CurrentDirectory, "TabExports");
    Directory.CreateDirectory(outDir);
    var surfaces = GetSurfacesByName(Tabs, "Chart");
    for (int i = 0; i < surfaces.Count; i++)
    {
        var file = Path.Combine(outDir, $"tab_{i + 1:D2}.png");
        surfaces[i].ExportToFile(file, ExportType.Png, false, size);
    }
}
private static List<SciChartSurface> GetSurfacesByName(TabControl tabs, string surfaceName)
{
    var result = new List<SciChartSurface>();
    foreach (var item in tabs.Items)
    {
        if (item is TabItem ti && ti.Content is FrameworkElement root)
        {
            if (root is SciChartSurface surface)
            {
                result.Add(surface);
                continue;
            }
            if (root.FindName(surfaceName) is SciChartSurface scs)
                result.Add(scs);
        }
    }
    return result;
}

Showing or Hiding Cursors on an Exported Chart

The key idea is to access the relevant ChartModifier from the cloned SciChartSurface instance and adjust its properties before returning the clone for export.

The following code snippet demonstrates how to retrieve a CursorModifier from the cloned surface and disable it:

Disabling a CursorModifier
Copy Code
protected override SciChartSurfaceBase CreateCloneOfSurfaceInMemory(Size newSize)
{
    // Perform base cloning functionality
    var cloned = (SciChartSurface)base.CreateCloneOfSurfaceInMemory(newSize);
    // Getting and disabling single CursorModifier
    var clonedCursor = ((ModifierGroup)this.ChartModifier).ChildModifiers.FirstOrDefault((x) => x.GetType() == typeof(CursorModifier));
    clonedCursor.IsEnabled = false
    // Or simply clear all Modifiers (notice Legend will be also missed)
    // ((ModifierGroup)cloned.ChartModifier).ChildModifiers.Clear();
    // Return cloned surface
    return cloned;
}

Adding Annotations or Visual Elements to an Exported Chart

Adding Annotations, RenderableSeries, or other visual elements directly to the exported chart allows you to modify the output without affecting the original SciChartSurface.

The example below demonstrates how to add a watermark as a TextAnnotation to the exported image:

Adding a watermark
Copy Code
public class SciChartSurfaceEx : SciChartSurface
{
    protected override SciChartSurfaceBase CreateCloneOfSurfaceInMemory(Size newSize)
    {
        // Create the default clone
        var cloned = (SciChartSurface)base.CreateCloneOfSurfaceInMemory(newSize);
        // Add a watermark only to the clone
        // May be useful for docs
        // Also you may add any other annotation like this to a resulted surface
        cloned.Annotations.Add(new TextAnnotation
        {
            Text = "Watermark text here!",
            CoordinateMode = AnnotationCoordinateMode.Relative,
            X1 = 0.9, Y1 = 0.9,
            HorizontalAnchorPoint = HorizontalAnchorPoint.Right,
            VerticalAnchorPoint = VerticalAnchorPoint.Bottom,
            Opacity = 0.5
        });
        // Return the modified clone; the original surface remains unchanged
        return cloned;
    }
}

Placing Custom UI Elements on an Exported Chart

It is possible to add and position any WPF UI elements on a cloned SciChartSurface using the CustomAnnotation, which allows embedding custom content and positioning it through the Annotations API.

The example below shows how to use CustomAnnotation to overlay a TextBlock on the exported chart without affecting the original surface:

Overlaying a TextBlock
Copy Code
using SciChart.Charting.Visuals.Annotations;
// Any WPF control you want to export along with the chart
var myControl = new System.Windows.Controls.TextBlock
{
    Text = "Extra info",
    FontSize = 16
};
// Place it at a desired chart coordinate (e.g., relative 0..1)
var custom = new CustomAnnotation
{
    Content        = myControl,
    X1             = 0.02,
    Y1             = 0.98,
    CoordinateMode = AnnotationCoordinateMode.Relative
};
sciChartSurface.Annotations.Add(custom);
// Perform export
sciChartSurface.ExportToFile("chart_with_control.png", ExportType.Png, useXamlRenderSurface: false, exportedSize: null);

Changing the Theme or Background Color for Export

If your original chart uses a dark theme, but you need to export images (e.g., for documents or reports) with a light theme, without changing the theme on the original SciChartSurface, you can achieve this using the following approach:

Exporting with a light theme
Copy Code
public class SciChartSurfaceEx : SciChartSurface
{
    protected override SciChartSurfaceBase CreateCloneOfSurfaceInMemory(Size newSize)
    {
        // Create the default clone
        var cloned = (SciChartSurface)base.CreateCloneOfSurfaceInMemory(newSize);
        // Change theme on the CLONED surface
        ThemeManager.SetTheme(cloned, "Chrome");
        // Return the modified clone; the original surface remains unchanged
        return cloned;
    }
}

Performing Export from a Background Thread

Since export relies on WPF visuals, it must be executed on an STA (Single Threaded Apartment) UI thread. If you're initiating the export from a background thread, you’ll need to marshal the call to the UI thread using the Dispatcher.

The example below demonstrates how to do this:

Exporting from a background thread using the Dispatcher
Copy Code
await Application.Current.Dispatcher.InvokeAsync(() =>
{
    sciChartSurface.ExportToFile("chart.png", ExportType.Png, false, null);
});

For headless/off-screen export (no UI), run your exporter on a dedicated STA thread that you create:

Off-screen export from a dedicated STA thread
Copy Code
var t = new Thread(() =>
{
    // Build surface, Measure/Arrange, Export...
})
{ IsBackground = true };
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();

Copying a Chart to the Clipboard

You can easily export a chart to a Bitmap and copy it to the Clipboard:

Export to Clipboard
Copy Code
var bmp = sciChartSurface.ExportToBitmapSource(false, null);
System.Windows.Clipboard.SetImage(bmp);

Exporting a Video of a Real-Time Updating Chart

SciChart does not support direct video export. However, a common approach is to:

  1. Render a sequence of images, one per frame, by capturing the chart as it updates
  2. Encode the image sequence into a video using a tool like FFmpeg or another video encoder

This method captures exactly what is displayed on screen for each frame, allowing you to export dynamic charts or animations as video content.

Exporting a sequence of images
Copy Code
public async Task ExportFramesOnScreenAsync(
    SciChartSurface sciChartSurface,
    IXyDataSeries<double, double> dataSeries,
    string framesDir,
    int frameCount = 300,
    int fps = 30)
{
    Directory.CreateDirectory(framesDir);
    // Animate a sine wave by phase
    for (int i = 0; i < frameCount; i++)
    {
        double phase = i * 0.1;
        using (sciChartSurface.SuspendUpdates())
        {
            dataSeries.Clear();
            for (int x = 0; x <= 1000; x++)
            {
                double y = Math.Sin(2.0 * Math.PI * (x / 100.0) + phase);
                dataSeries.Append(x, y);
            }
        }
        string path = Path.Combine(framesDir, $"frame_{i:0000}.png");
        // Export current on-screen state as PNG
        sciChartSurface.ExportToFile(path, ExportType.Png, false);
        await Task.Delay(1000 / fps);
    }
}

You can also use off-screen export to simulate changes in the chart or its content. The main idea is to generate a sequence of images, each representing a frame, which can later be encoded into a video.

There are many third-party libraries available to help with encoding images into a video file. Alternatively, you can use a terminal-based tool like FFmpeg.

To do this, open a terminal in the folder containing your exported frames (e.g., frame_0000.png, frame_0001.png, etc.) and run one of the following commands:

Generating an MP4 file (H.264 encoding, widely compatible)

ffmpeg -framerate 30 -i frame_%04d.png -c:v libx264 -pix_fmt yuv420p -crf 18 -preset slow output.mp4

Exporting a 3D Chart

Exporting a 3D chart in WPF requires a slightly different approach than 2D rendering. Because SciChart 3D relies on real-time GPU rendering, it can only capture a raster image of the chart exactly as it appears on screen. In other words, the chart must be visible and fully rendered in the viewport when the export command runs, and the output will reflect the current camera angle, zoom level, and any active visual effects.

Exporting a 3D Chart
Copy Code
// Assumes sciChartSurface3D is visible on screen
string fileName = Path.Combine(Environment.CurrentDirectory, "chart3d.png");
sciChartSurface3D.ExportToFile(fileName, ExportType.Png);

See Also